package com.yami.shop.platform.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.yami.shop.bean.dto.UploadDto;
import com.yami.shop.common.bean.Minio;
import com.yami.shop.common.constants.BizCacheNames;
import com.yami.shop.common.response.ServerResponseEntity;
import com.yami.shop.common.util.MinIoUtils;
import com.yami.shop.common.util.RedisUtil;
import com.yami.shop.config.ShopConfig;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.time.format.DateTimeFormatter;
import java.util.*;


/**
 * @ClassName MinioController
 * @Description TODO
 * @Author Ryan.
 * @Emial_AND_OICQ ryan.tang@cyberwisdom.net AND 100792057
 * @Date 2020/9/8 9:24
 * @Version 1.0
 */
@RestController
@RequestMapping("/ua/upload")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class MinioController {
    /**
     * 假装这是一个数据库，用来存放已经上传过的数据信息
     */
//    private ConcurrentHashMap<String,UploadDto> mysql = new ConcurrentHashMap<>();

    /**
     * 上传成功
     */
    private final Integer UPLOAD_SUCCESS = 0;

    @Autowired
    private ShopConfig shopConfig;
    /**
     * 部分上传
     */
    private final Integer UPLOAD_PART = 1;
    /**
     * 上传的文件夹(根据时间确定)
     */
    public static final String NORM_DAY_PATTERN = "yyyy/MM/dd";

    /**
     * 初始化大文件上传
     * 未做优化，业务逻辑请根据实际情况修改
     *
     * @param uploadDto
     * @return
     */
    @PostMapping("init-chunk-upload")
    public ServerResponseEntity<Map<String, Object>> initChunkUpload(@RequestBody UploadDto uploadDto) {
        //校验文件md5，该文件是否上传过
//        if(mysql.containsKey(uploadDto.getFileMd5())){
        //minio 初始化
        Minio minio = shopConfig.getMinio();
        MinIoUtils.init(minio);
        if (RedisUtil.hasKey(BizCacheNames.BIG_BIZ + uploadDto.getFileMd5())) {
            UploadDto mysqlFileData = RedisUtil.get(BizCacheNames.BIG_BIZ + uploadDto.getFileMd5());
            //秒传
            if (UPLOAD_SUCCESS.equals(mysqlFileData.getUploadStatus())) {
                Map<String, Object> map = new HashMap<>();
                map.put("code", UPLOAD_SUCCESS);
                map.put("data", mysqlFileData);
                return ServerResponseEntity.success(map);
            }
            //续传
            //获取到该文件已上传分片
            Map<Integer, String> okChunkMap = MinIoUtils.mapChunkObjectNames(uploadDto.getBucketName(), uploadDto.getFileMd5());
            List<UploadDto> chunkUploadUrls = new ArrayList<>();
            if (okChunkMap != null && okChunkMap.size() > 0) {
                for (int i = 1; i <= uploadDto.getChunkCount(); i++) {
                    //判断当前分片是否已经上传过了
                    if (!okChunkMap.containsKey(i)) {
                        //生成分片上传url
                        UploadDto url = new UploadDto();
                        url.setPartNumber(i);
                        url.setUploadUrl(MinIoUtils.createUploadChunkUrl(uploadDto.getBucketName(), uploadDto.getFileMd5(), i));
                        chunkUploadUrls.add(url);
                    }
                }
                if (chunkUploadUrls.size() == 0) {
                    Map<String, Object> map = new HashMap<>();
                    map.put("code", 200);
                    map.put("msg", "所有分片已经上传完成，仅需要合并文件");
                    return ServerResponseEntity.success(map);
                }
                Map<String, Object> map = new HashMap<>();
                map.put("code", mysqlFileData.getUploadStatus());
                map.put("data", chunkUploadUrls);
                return ServerResponseEntity.success(map);
            }
        }
        //初次上传和已有文件信息但未上传任何分片的情况下则直接生成所有上传url
        List<String> uploadUrls = MinIoUtils.createUploadChunkUrlList(uploadDto.getBucketName(), uploadDto.getFileMd5(), uploadDto.getChunkCount());
        List<UploadDto> chunkUploadUrls = new ArrayList<>();
        for (int i = 1; i <= uploadUrls.size(); i++) {
            UploadDto url = new UploadDto();
            url.setPartNumber(i);
            url.setUploadUrl(MinIoUtils.createUploadChunkUrl(uploadDto.getBucketName(), uploadDto.getFileMd5(), i));
            chunkUploadUrls.add(url);
        }
        //向数据库中记录该文件的上传信息
        uploadDto.setUploadStatus(UPLOAD_PART);
//        mysql.put(uploadDto.getFileMd5(),uploadDto);
        RedisUtil.set(BizCacheNames.BIG_BIZ + uploadDto.getFileMd5(), uploadDto, 172800);
        Map<String, Object> map = new HashMap<>();
        map.put("code", UPLOAD_PART);
        map.put("data", chunkUploadUrls);
        return ServerResponseEntity.success(map);
    }

    @PostMapping("init-chunk-upload-min")
    public ServerResponseEntity<Map<String, Object>> initChunkUploadMin(@RequestBody UploadDto uploadDto) {
        //校验文件md5，该文件是否上传过
        //初次上传和已有文件信息但未上传任何分片的情况下则直接生成所有上传url
        String dir = DateUtil.format(new Date(), NORM_DAY_PATTERN) + "/";
        String fileName = IdUtil.simpleUUID();
        String fileNameT = uploadDto.getFileName();
        String suffix = fileNameT.substring(fileNameT.lastIndexOf("."));
        fileName = dir + fileName + suffix;
        Minio minio = shopConfig.getMinio();
        MinIoUtils.init(minio);
        String uploadUrl = MinIoUtils.createUploadUrl(uploadDto.getBucketName(), fileName);
        Map<String, Object> map = new HashMap<>();
        map.put("code", UPLOAD_PART);
        map.put("data", uploadUrl);
        return ServerResponseEntity.success(map);
    }

    /**
     * 合并文件并返回文件信息
     *
     * @param uploadDto
     * @return
     */
    @PostMapping("compose-file")
    public ServerResponseEntity<UploadDto> composeFile(@RequestBody UploadDto uploadDto) {
        //根据md5获取所有分片文件名称(minio的文件名称 = 文件path)
        Minio minio = shopConfig.getMinio();
        MinIoUtils.init(minio);
        List<String> chunks = MinIoUtils.listObjectNames(uploadDto.getBucketName(), uploadDto.getFileMd5());
        //自定义文件名称
        String fileName = uploadDto.getFileName();
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        DateTimeFormatter df = DateTimeFormatter.ofPattern("HHmmss");//yyyyMMdd
        String dir = DateUtil.format(new Date(), NORM_DAY_PATTERN) + "/";
//        fileName = dir+ df.format(LocalDateTime.now()) +fileName ;
        fileName = dir + uploadDto.getFileMd5() + suffix;
        //合并文件
        if (MinIoUtils.composeObject(uploadDto.getBucketName(), chunks, fileName, uploadDto.getContentType())) {
            try {
                MinIoUtils.remove(uploadDto.getBucketName(), uploadDto.getFileMd5() + "/");
            } catch (Exception e) {
                e.printStackTrace();
            }
            //获取文件访问外链(1小时过期)
            String url = MinIoUtils.getObjectUrl(uploadDto.getBucketName(), fileName, 7);
            //获取数据库里记录的文件信息，修改数据并返回文件信息
            UploadDto dbData = RedisUtil.get(BizCacheNames.BIG_BIZ + uploadDto.getFileMd5());
            dbData.setUploadStatus(UPLOAD_SUCCESS);
            dbData.setFilePath(url);
            dbData.setSuffix(suffix);
//            mysql.put(uploadDto.getFileMd5(), dbData);
            RedisUtil.set(BizCacheNames.BIG_BIZ + uploadDto.getFileMd5(), dbData, 172800);
            return ServerResponseEntity.success(dbData);
        }
        return ServerResponseEntity.showFailMsg("合并文件失败");
    }

    @PostMapping("compose-file1")
    public ServerResponseEntity<Void> composeFile1(@RequestBody UploadDto uploadDto) {
        //根据md5获取所有分片文件名称(minio的文件名称 = 文件path)
        try {
            Minio minio = shopConfig.getMinio();
            MinIoUtils.init(minio);
            MinIoUtils.remove(uploadDto.getBucketName(), uploadDto.getFileMd5() + "/");
            return ServerResponseEntity.showFailMsg("删除成功");
        } catch (Exception e) {
            e.printStackTrace();
            return ServerResponseEntity.showFailMsg("删除失败");
        }
    }

    @PostMapping("ua/undata")
    public ServerResponseEntity<String> composeFile1(@RequestParam("multipartFile") MultipartFile multipartFile, @RequestParam("bucketName") String bucketName) {
        //根据md5获取所有分片文件名称(minio的文件名称 = 文件path)
        try {
            Minio minio = shopConfig.getMinio();
            MinIoUtils.init(minio);
            String uploadObject = MinIoUtils.uploadObject(multipartFile, bucketName);
            return ServerResponseEntity.success(uploadObject);
        } catch (Exception e) {
            e.printStackTrace();
            return ServerResponseEntity.showFailMsg("失败");
        }
    }

}
